package com.tridion.utilities.verifier.analyzer;

import com.tridion.utilities.verifier.model.ContentDeliveryInstallation;
import com.tridion.utilities.verifier.model.StatusMessage;
import com.tridion.utilities.verifier.model.StatusMessage.MessageType;
import com.tridion.utilities.verifier.model.JarFile;
import com.tridion.utilities.verifier.model.internal.Release;
import com.tridion.utilities.verifier.model.internal.Root;
import com.tridion.utilities.verifier.utils.ReleasesUtil;

/**
 * Analyzer that the exact versions of the CD Installation.
 * 
 * @author Daniel Neagu
 */
public class VersionAnalyzer implements Analyzer {

    private static final String CD_MAIN_LIB = "cd_core.jar";

    /**
     * Analyzes the versions of the CD installation and establishes the installed hotfixes.
     */
    @Override
    public void analyze(final ContentDeliveryInstallation cdi) {
        // let's check the version of the cd_core.jar
        final String cdCoreVersion = getJarVersion(CD_MAIN_LIB, cdi);
        
        // let's get the main version of the installation
        final String cdMainVersion = getMainVersion(cdCoreVersion);
        
        // let's get the list of all possible versions
        final Root allVersions = ReleasesUtil.loadReleases();
        
        // let's identify the release
        final Release release = getRelease(cdMainVersion, allVersions);
        
        // set it on our installation object
        cdi.setRelease(release);
        
        // sets the TridionSpecific flag and the build number on the tridion specific jars 
        processTridionSpecificJars(cdi, release);
        
        // check if all jars present in the CDInstallation but also in the release have at least
        // the version from the release
        checkRelease(cdi, release);
        
        // let's check the maximum build number for the installation libraries
        final int maxBuildNumber = getMaxBuildNumber(cdi);
        
        // now let's identify the exact minor version
        final Release minorRelease = getMinorVersion(maxBuildNumber, release);
        
        // set it on our installation object
        cdi.setMinorRelease(minorRelease);
        
        // check if all jars present in the CDInstallation but also in the minor release have at least
        // the version from the minor release
        checkRelease(cdi, minorRelease);
    }
    
    private static Release getMinorVersion(final int maxBuildNumber, final Release release) {
        Release mR = null;
        
        for (Release minorRelease : release.getMinorReleases()) {
            if (maxBuildNumber >= minorRelease.getBuild()) {
                mR = minorRelease;
            }
        }
        
        
        return mR;
    }

    private static String getJarVersion(final String jarName, final ContentDeliveryInstallation cdi) {
        // loop through all jar files and get the version of the jar we're interested in
        for (JarFile file : cdi.getJars()) {
            if (jarName.equals(file.getName())) {
                return file.getVersion();
            }
        }
        
        return "unknown";
    }
    
    private static int getMaxBuildNumber(final ContentDeliveryInstallation cdi) {
        int build = 0;
        
        // loop through all jar files and get the biggest build number
        for (JarFile file : cdi.getJars()) {
            if (file.isTridionSpecific() && file.getBuild() > build) {
                build = file.getBuild();
            }
        }
        
        return build;
    }
    
    private static String getMainVersion(final String fileVersion) {
        if (fileVersion.contains(".")) {
            final String[] versionBits = fileVersion.split("\\.");
            
            if (versionBits.length > 1) {
                return versionBits[0] + "." + versionBits[1];
            }
        }
        
        return "unknown";
    }
    
    /**
     * Loops through all jars of an existing installation and marks all cd_xxx.jar jar files with
     * TridionSpecific flag and sets the right build number (equal with the manifest version).
     */
    private static void processTridionSpecificJars(final ContentDeliveryInstallation cdi, final Release release) {
        // loop throu the list of tridion jars and makes sure we add information about the installed jars
        for (JarFile jarFile : release.getJars()) {
            if (jarFile.isTridionSpecific()) {
                // this jar is tridion specific...need to check if it's also present in the installation
                for (JarFile installedFile : cdi.getJars()) {
                    if (installedFile.getName().equals(jarFile.getName())) {
                        // this installed file is also in the installation
                        // we need to set the tridionSpecific flag and the build number
                        installedFile.setTridionSpecific(true);
                        installedFile.setBuild(getBuildNumber(installedFile.getVersion()));
                    }
                }
            }
        }
    }
    
    private static int getBuildNumber(final String versionString) {
        if (versionString.contains(".")) {
            final String[] versionBits = versionString.split("\\.");
            
            if (versionBits.length > 1) {
                final String buildString = versionBits[versionBits.length - 1];
                try {
                    final int build = Integer.parseInt(buildString);
                    
                    return build;
                } catch (NumberFormatException nfe) {
                    nfe.printStackTrace();
                    //TODO:
                }
            }
        }
        
        return 0;
    }
    
    private Release getRelease(final String cdMainVersion, final Root allVersions) {
        for (Release release : allVersions.getReleases()) {
            if (cdMainVersion.equals(release.getVersion())) {
                return release;
            }
        }
        
        return null;
    }
    
    private static void checkRelease(final ContentDeliveryInstallation cdi, final Release release) {
        for (JarFile jar : cdi.getJars()) {
            if (jar.isTridionSpecific()) {
                final int installedBuildNumber = jar.getBuild();
                final String installedChecksum = jar.getChecksum();
                
                final JarFile releasedJar = getJar(jar.getName(), release);
                
                if (releasedJar != null) {//jar also in the release
                    // let's get the build number of the jar in the release
                    final int releasedJarBuild = releasedJar.getBuild();
                    
                    if (installedBuildNumber < releasedJarBuild) {
                        final StatusMessage sm = new StatusMessage(MessageType.ERROR, "Installed version smaller " +
                                "than the one included in " + release.getName(), jar);
                        cdi.addMessage(sm);
                        jar.setStatusMessage(sm);
                    } else if (installedBuildNumber == releasedJarBuild) {// same as released jar
                        final String releasedJarChecksum = releasedJar.getChecksum();
                        
                        if (installedChecksum.equals(releasedJarChecksum)) {
                            final StatusMessage sm = new StatusMessage(MessageType.INFO, "Valid", jar);
                            cdi.addMessage(sm);
                            jar.setStatusMessage(sm);
                        } else {
                            final StatusMessage sm = new StatusMessage(MessageType.ERROR, "Installed checksum " +
                            		"different than the one included in " + release.getName(), jar);
                            cdi.addMessage(sm);
                            jar.setStatusMessage(sm);
                        }
                    }// probably build from a hotfix
                } else {
                    // the jar file is not in this release...probably it's in the parent one
                }
            }
        }
    }
    
    private static JarFile getJar(final String jarName, final Release release) {
        for (JarFile jar : release.getJars()) {
            if (jarName.equals(jar.getName())) {
                return jar;
            }
        }
        
        return null;
    }
}
